//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-04-13     ZhangYihua   first version
//
// Description  : output one-port dat selected from n-port input dat.
//################################################################################

module n2o #(
parameter           PNUM                    = 8,            // source port number
parameter           DWID                    = 16,           // data width of one port
parameter           SMODE                   = "ONEHOT",     // sel is onehot signal
//parameter           SMODE                   = "BINARY",     // sel is binary signal

// the following parameters are calculated automatically
parameter           SWID                    = (SMODE=="ONEHOT") ? PNUM         :
                                              (SMODE=="BINARY") ? $clog2(PNUM) :
                                                                  0              // invalid SMODE
) ( 
input               [SWID-1:0]              sel,        // support one-hot and all-zero input when SMODE="ONEHOT"
input               [DWID*PNUM-1:0]         np_dat,
output              [DWID-1:0]              op_dat
);

//################################################################################
// define local varialbe and localparam
//################################################################################


//################################################################################
// main
//################################################################################

generate if (SMODE=="ONEHOT") begin:G_ONEHOT
    
    reg                 [PNUM-1:0]              dat_ary[DWID-1:0];
    reg                 [DWID-1:0]              op_dat_o;

    always@(*) begin:RESORT
        integer         i;
        integer         j;

        // mask unselected port dat
        for (i=0;i<DWID;i=i+1) begin
            for (j=0;j<PNUM;j=j+1) begin
                dat_ary[i][j] = np_dat[DWID*j+i] & sel[j];
            end
        end
    end

    always@(*) begin:SEL
        integer         i;

        // OR all port dat for better timing
        for (i=0;i<DWID;i=i+1) begin
            op_dat_o[i] = |dat_ary[i][PNUM-1:0];
        end
    end

    assign op_dat = op_dat_o;

end else if (SMODE=="BINARY") begin:G_BINARY

    localparam          EPNUM                   = 1<<SWID;

    reg                 [DWID-1:0]              dat_ary[EPNUM-1:0];

    always@(*) begin:EXPAND
        integer         i;

        for (i=0;i<PNUM;i=i+1) begin
            dat_ary[i] = np_dat[DWID*i+:DWID];
        end

        for (i=PNUM;i<EPNUM;i=i+1) begin
`ifdef ARRAY_ADDR_OUT_OF_RANGE_RD_X
            dat_ary[i] = {DWID{1'bx}};      // for better area and timing
`else
            dat_ary[i] = {DWID{1'b0}};
`endif
        end
    end

    assign op_dat = dat_ary[sel];
    
end else begin:G_SMODE_NULL
`ifdef PARAMETER_NULL_CHECK  
    // if 'SMODE' wrong, force compiler to error for attention.
    smode_null u_null();
`endif
end endgenerate

`ifdef CBB_ASSERT_ON
// synopsys translate_off

//generate if (SMODE=="ONEHOT") begin:AG_OH
//
//reg     ONEHOT_CHKEN;
//
//a_sel_onehot: assert property (
//    ((ONEHOT_CHKEN!==1'b0)&(sel>0) |-> $onehot(sel))
//) else begin
//    $error("input port 'sel' %0b is not one-hot", sel);
//end
//    
//end endgenerate

// synopsys translate_on
`endif

endmodule
